import { GraphStyle } from "@/utils/styles";

interface OptionType {
  beforeAdd: (model: object, type: string) => Promise<any> | undefined;
  afterAdd: (model: object, type: string) => Promise<any> | undefined;
}

export function dragEdge(G6, option: OptionType) {
  G6.registerBehavior("drag-edge", {
    getDefaultCfg() {
      return {
        updateEdge: true,
        delegate: true,
        delegateStyle: {},
        dragEdge: false,
      };
    },
    getEvents() {
      return {
        "anchor:dragstart": "onDragStart",
        "anchor:drag": "onDrag",
        "anchor:dragenter": "onDragEnter",
        "anchor:dragleave": "onDragLeave",
        "anchor:dragend": "onDragEnd",
      };
    },
    onDragStart(e) {
      const node = e.target.getParent().getParent().get("item");
      const anchor = e.item;
      const anchorIndex = anchor.index;
      const point = node.getAnchorPoints()[anchorIndex];

      this.target = e.item;
      this.origin = {
        x: point.x,
        y: point.y,
        sourceNode: node,
        sourceAnchor: anchorIndex,
      };

      this.graph.getNodes().forEach((node) => {
        node.showAnchor(this.graph);
        node.getContainer().anchorShapes.forEach((a) => {
          a.get("item").showHotpot();
        });
      });

      this.graph.set("onDragEdge", true);
    },
    onDrag(e) {
      if (!this.origin) {
        return;
      }
      this._updateEdge(this.target, e);
    },
    onDragEnter(e) {
      if (!this.origin) {
        return;
      }
      if (!this.sameNode(e)) {
        e.item.setHotspotActived(true);
        this.origin.targetNode = e.target.getParent().getParent().get("item");
        this.origin.targetAnchor = e.item.get("index");
      }
    },
    onDragLeave(e) {
      if (!this.origin) {
        return;
      }
      if (!this.sameNode(e)) {
        e.item.setHotspotActived(false);
        this.origin.targetNode = null;
        this.origin.targetAnchor = null;
      }
    },
    onDragEnd(e) {
      if (!this.origin) {
        return;
      }
      const delegateShape = e.item.get("edgeDelegate");
      if (delegateShape) {
        delegateShape.remove();
        this.target.set("edgeDelegate", null);
      }
      this._updateEdge(this.target, e, true);
      this.target = null;
      this.origin = null;
      this.graph.set("onDragEdge", false);

      this.graph.getNodes().forEach((node) => {
        node.hideAnchor(this.graph);
        node.getContainer().anchorShapes.forEach((a) => {
          a.get("item").hideHotpot();
        });
      });
    },
    sameNode(e) {
      return (
        e.target.type === "marker" &&
        e.target.getParent() &&
        e.target.getParent().getParent().get("item").get("id") === this.origin.sourceNode.get("id")
      );
    },
    _updateEdge(item, e, force) {
      const x = e.x;
      const y = e.y;
      if (this.delegate && !force) {
        this._updateEdgeDelegate(item, x, y);
        return;
      }
      this._addEdge(e);
      this.graph.paint();
    },
    _updateEdgeDelegate(item, x, y) {
      const self = this;
      let edgeShape = item.get("edgeDelegate");
      if (!edgeShape) {
        const parent = self.graph.get("group");
        edgeShape = parent.addShape("line", {
          attrs: {
            x1: this.origin.x,
            y1: this.origin.y,
            x2: x,
            y2: y,
            ...GraphStyle.edgeDelegationStyle,
          },
        });
        edgeShape.set("capture", false);
        item.set("edgeDelegate", edgeShape);
      }
      edgeShape.attr({ x2: x, y2: y });
      this.graph.paint();
    },
    async _addEdge() {
      if (this.origin.targetNode) {
        const addModel = {
          clazz: "flow",
          source: this.origin.sourceNode.get("id"),
          target: this.origin.targetNode.get("id"),
          sourceAnchor: this.origin.sourceAnchor,
          targetAnchor: this.origin.targetAnchor,
        };
        if (this.graph.executeCommand) {
          this.graph.executeCommand("add", {
            type: "edge",
            addModel: addModel,
          });
        } else {
          try {
            if (!!option.beforeAdd) {
              await option.beforeAdd(addModel, "edge");
            }
            this.graph.emit("add-edge:before");
            this.graph.add("edge", addModel);
            this.graph.emit("add-edge:after");
            if (!!option.afterAdd) {
              await option.afterAdd(addModel, "edge");
            }
          } catch (e) {
            console.error(e);
          }
        }
      }
    },
  });
}
